package com.mkx.filter;

import com.mkx.security.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
 * 网关认证过滤器，实现动态鉴权
 */
@Component
public class AuthFilter implements GlobalFilter, Ordered {

    @Autowired
    private JwtUtils jwtUtils;

    // 公开接口，无需认证
    private static final Set<String> PUBLIC_PATHS = new HashSet<>(Arrays.asList(
            "/api/auth/login",
            "/api/auth/logout",
            "/api/auth/refresh",
            "/actuator/**"
    ));

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();

        // 检查是否为公开接口
        if (isPublicPath(path)) {
            return chain.filter(exchange);
        }

        // 从请求头获取令牌
        String token = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (token == null || !token.startsWith("Bearer ")) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        // 提取令牌
        token = token.substring(7);

        // 验证令牌
        if (!jwtUtils.validateToken(token)) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }

        try {
            // 解析令牌获取用户信息
            String userId = jwtUtils.getUserIdFromToken(token);
            String tenantId = jwtUtils.getTenantIdFromToken(token);
            String role = jwtUtils.getRoleFromToken(token);
            String[] permissions = jwtUtils.getPermissionsFromToken(token);

            // 动态鉴权：检查用户是否有权限访问该接口
            if (!hasPermission(path, role, permissions)) {
                exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                return exchange.getResponse().setComplete();
            }

            // 将用户信息添加到请求头，传递给下游服务
            ServerWebExchange modifiedExchange = exchange.mutate()
                    .request(builder -> builder
                            .header("X-User-Id", userId)
                            .header("X-Tenant-Id", tenantId)
                            .header("X-User-Role", role)
                    )
                    .build();

            return chain.filter(modifiedExchange);
        } catch (Exception e) {
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
    }

    /**
     * 判断是否为公开接口
     */
    private boolean isPublicPath(String path) {
        for (String publicPath : PUBLIC_PATHS) {
            if (path.startsWith(publicPath) || path.equals(publicPath)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查用户是否有权限访问该接口
     */
    private boolean hasPermission(String path, String role, String[] permissions) {
        // 平台管理员具有所有权限
        if ("PLATFORM_ADMIN".equals(role)) {
            return true;
        }

        // 租户管理员具有租户内的所有权限
        if ("TENANT_ADMIN".equals(role) && path.startsWith("/api/tenant")) {
            return true;
        }

        // 普通用户需要特定权限
        if (permissions != null && permissions.length > 0) {
            // 简单的权限判断逻辑，可以根据实际需求扩展
            for (String permission : permissions) {
                // 这里可以实现更复杂的权限匹配逻辑
                if (path.contains(permission.toLowerCase())) {
                    return true;
                }
            }
        }

        return false;
    }

    @Override
    public int getOrder() {
        // 设置过滤器的执行顺序，数字越小优先级越高
        return 0;
    }
}